home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 25 / CU Amiga Magazine's Super CD-ROM 25 (1998)(EMAP Images)(GB)(Track 1 of 2)[!][issue 1998-08].iso / CUCD / Utilities / Type1Manager / src / paths.c < prev    next >
C/C++ Source or Header  |  1996-07-12  |  39KB  |  1,584 lines

  1. /* $XConsortium: paths.c,v 1.4 91/10/10 11:18:40 rws Exp $ */
  2. /* Copyright International Business Machines, Corp. 1991
  3.  * All Rights Reserved
  4.  * Copyright Lexmark International, Inc. 1991
  5.  * All Rights Reserved
  6.  *
  7.  * License to use, copy, modify, and distribute this software and its
  8.  * documentation for any purpose and without fee is hereby granted,
  9.  * provided that the above copyright notice appear in all copies and that
  10.  * both that copyright notice and this permission notice appear in
  11.  * supporting documentation, and that the name of IBM or Lexmark not be
  12.  * used in advertising or publicity pertaining to distribution of the
  13.  * software without specific, written prior permission.
  14.  *
  15.  * IBM AND LEXMARK PROVIDE THIS SOFTWARE "AS IS", WITHOUT ANY WARRANTIES OF
  16.  * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO ANY
  17.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
  18.  * AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.  THE ENTIRE RISK AS TO THE
  19.  * QUALITY AND PERFORMANCE OF THE SOFTWARE, INCLUDING ANY DUTY TO SUPPORT
  20.  * OR MAINTAIN, BELONGS TO THE LICENSEE.  SHOULD ANY PORTION OF THE
  21.  * SOFTWARE PROVE DEFECTIVE, THE LICENSEE (NOT IBM OR LEXMARK) ASSUMES THE
  22.  * ENTIRE COST OF ALL SERVICING, REPAIR AND CORRECTION.  IN NO EVENT SHALL
  23.  * IBM OR LEXMARK BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
  24.  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  25.  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  26.  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
  27.  * THIS SOFTWARE.
  28.  */
  29.  
  30. /*
  31.  * PATHS Module - Path Operator Handler
  32.  *
  33.  * This is the module that is responsible for building and transforming
  34.  * path lists.
  35.  *
  36.  * author: Jeffrey B. Lotspiech (lotspiech@almaden.ibm.com)
  37.  */
  38.  
  39.  
  40. #ifndef T1GST
  41. #include "global.h"
  42. #endif
  43.  
  44.  
  45. /*
  46.  * Local prototypes
  47.  */
  48. static struct segment *ReverseSubPath(struct segment *p);
  49. static struct segment *DropSubPath(struct segment *p0);
  50. static struct segment *SplitPath(struct segment *anchor, struct segment *before);
  51. static void UnClose(struct segment *p0);
  52.  
  53.  
  54. /*
  55.  * When 'link' is NULL, we are at the last segment in the path (surprise!).
  56.  *
  57.  * 'last' is only non-NULL on the first segment of a path,
  58.  * for all the other segments 'last' == NULL.  We test for a non-NULL
  59.  * 'last' (ISPATHANCHOR predicate) when we are given an alleged path
  60.  * to make sure the user is not trying to pull a fast one on us.
  61.  *
  62.  * A path may be a collection of disjoint paths.  Every break in the
  63.  * disjoint path is represented by a MOVETYPE segment.
  64.  */
  65.  
  66. /*
  67.  * CopyPath() - Physically Duplicating a Path
  68.  *
  69.  * This simple function illustrates moving through the path linked list.
  70.  * Duplicating a segment just involves making a copy of it, except for
  71.  * text, which has some auxiliary things involved.  We don't feel
  72.  * competent to duplicate text in this module, so we call someone who
  73.  * knows how (in the FONTS module).
  74.  */
  75. struct segment *t1_CopyPath(struct segment *p0)
  76. {
  77.     struct segment *p, *n, *last, *anchor;
  78.  
  79.     for (p = p0, anchor = NULL; p != NULL; p = p->link)
  80.     {
  81.         ARGCHECK((!ISPATHTYPE(p->type) || ((p != p0) && (p->last != NULL))),
  82.         "CopyPath: invalid segment", p, NULL, (0,NULL,NULL,NULL), struct segment *);
  83.  
  84.         if (p->type == TEXTTYPE)
  85.             n = (struct segment *)CopyText(p);
  86.         else
  87.             n = (struct segment *)Allocate(p->size, p, 0);
  88.         n->last = NULL;
  89.         if (anchor == NULL)
  90.             anchor = n;
  91.         else
  92.             last->link = n;
  93.         last = n;
  94.     }
  95. /*
  96.  * At this point we have a chain of newly allocated segments hanging off
  97.  * 'anchor'.  We need to make sure the first segment points to the last:
  98.  */
  99.     if (anchor != NULL)
  100.     {
  101.         n->link = NULL;
  102.         anchor->last = n;
  103.     }
  104.  
  105.     return (anchor);
  106. }
  107.  
  108.  
  109. /*
  110.  * KillPath() - Destroying a Path
  111.  *
  112.  * Destroying a path is simply a matter of freeing each segment in the
  113.  * linked list.  Again, we let the experts handle text.
  114.  */
  115. void t1_KillPath(struct segment *p)
  116. {
  117.     struct segment *linkp;    /* temp register holding next segment*/
  118.  
  119.     /* return conditional based on reference count 3-26-91 PNM */
  120.     if ((--(p->references) > 1) ||
  121.         ((p->references == 1) && !ISPERMANENT(p->flag)))
  122.         return;
  123.  
  124.     while (p != NULL)
  125.     {
  126.         if (!ISPATHTYPE(p->type))
  127.         {
  128.             ArgErr("KillPath: bad segment", p, NULL);
  129.             return;
  130.         }
  131.         linkp = p->link;
  132.         if (p->type == TEXTTYPE)
  133.             KillText(p);
  134.         else
  135.             Free(p);
  136.         p = linkp;
  137.     }
  138. }
  139.  
  140.  
  141. /*
  142.  * "location" Objects
  143.  *
  144.  * The TYPE1IMAGER user creates and destroys objects of type "location".  These
  145.  * objects locate points for the primitive path operators.  We play a trick
  146.  * here and store these objects in the same "segment" structure used for
  147.  * paths, with a type field == MOVETYPE.
  148.  *
  149.  * This allows the Line() operator, for example, to be very trivial:
  150.  * It merely stamps its input structure as a LINETYPE and returns it to the
  151.  * caller--assuming, of course, the input structure was not permanent (as
  152.  * it usually isn't).
  153.  *
  154.  * The "movesegment" Template Structure
  155.  *
  156.  * This template is used as a generic segment structure for Allocate:
  157.  */
  158.  
  159.  
  160. /* added reference field 1 to temporary template below 3-26-91 PNM */
  161. static struct segment movetemplate =
  162. {MOVETYPE, 0, 1, sizeof(struct segment), 0,
  163.  NULL, NULL, 0, 0};
  164.  
  165.  
  166. /*
  167.  * Loc() - Create an "Invisible Line" Between (0,0) and a Point
  168.  */
  169. struct segment *t1_Loc(
  170.         struct XYspace *S,    /* coordinate space to interpret X,Y */
  171.         double x, double y)    /* destination point */
  172. {
  173.     struct segment *r;
  174.  
  175.     IfTrace3((MustTraceCalls), "..Loc(S=%z, x=%f, y=%f)\n", S, &x, &y);
  176.  
  177.     r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
  178.     TYPECHECK("Loc", S, SPACETYPE, r, (0,NULL,NULL,NULL), struct segment *);
  179.  
  180.     r->last = r;
  181.     r->context = S->context;
  182.     (*S->convert) (&r->dest, S, x, y);
  183.     ConsumeSpace(S);
  184.     return (r);
  185. }
  186.  
  187.  
  188. /*
  189.  * ILoc() - Loc() With Integer Arguments
  190.  */
  191. struct segment *ILoc(
  192.         struct XYspace *S,    /* coordinate space to interpret X,Y */
  193.         int x, int y)        /* destination point */
  194. {
  195.     struct segment *r;
  196.  
  197.     IfTrace3((MustTraceCalls), "..ILoc(S=%z, x=%d, y=%d)\n",
  198.          S, (long)x, (long)y);
  199.     r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
  200.     TYPECHECK("Loc", S, SPACETYPE, r, (0,NULL,NULL,NULL), struct segment *);
  201.  
  202.     r->last = r;
  203.     r->context = S->context;
  204.     (*S->iconvert) (&r->dest, S, (long)x, (long)y);
  205.     ConsumeSpace(S);
  206.     return (r);
  207. }
  208.  
  209.  
  210. /*
  211.  * SubLoc() - Vector Subtraction of Two Location Objects
  212.  *
  213.  * This user operator subtracts two location objects, yielding a new
  214.  * location object that is the result.
  215.  *
  216.  * The symmetrical function AddLoc() is totally redundent with Join(),
  217.  * so it is not provided.
  218.  */
  219. struct segment *SubLoc(struct segment *p1, struct segment *p2)
  220. {
  221.     IfTrace2((MustTraceCalls), "SubLoc(%z, %z)\n", p1, p2);
  222.  
  223.     ARGCHECK(!ISLOCATION(p1), "SubLoc: bad first arg", p1, NULL, (0,NULL,NULL,NULL), struct segment *);
  224.     ARGCHECK(!ISLOCATION(p2), "SubLoc: bad second arg", p2, NULL, (0,NULL,NULL,NULL), struct segment *);
  225.  
  226.     p1 = UniquePath(p1);
  227.     p1->dest.x -= p2->dest.x;
  228.     p1->dest.y -= p2->dest.y;
  229.     ConsumePath(p2);
  230.     return (p1);
  231. }
  232.  
  233.  
  234. /*
  235.  * Straight Line Segments
  236.  *
  237.  * PathSegment() - Create a Generic Path Segment
  238.  *
  239.  * Many routines need a LINETYPE or MOVETYPE path segment, but do not
  240.  * want to go through the external user's interface, because, for example,
  241.  * they already know the "fractpel" destination of the segment and the
  242.  * conversion is unnecessary.  PathSegment() is an internal routine
  243.  * provided to the rest of TYPE1IMAGER for handling these cases.
  244.  */
  245. struct segment *t1_PathSegment(
  246.         int type,        /* LINETYPE or MOVETYPE */
  247.         fractpel x, fractpel y) /* where to go to, if known */
  248. {
  249.     struct segment *r;    /* newly created segment */
  250.  
  251.     r = (struct segment *)Allocate(sizeof(struct segment), &movetemplate, 0);
  252.  
  253.     r->type = type;
  254.     r->last = r;        /* last points to itself for singleton */
  255.     r->dest.x = x;
  256.     r->dest.y = y;
  257.     return (r);
  258. }
  259.  
  260.  
  261. /*
  262.  * Line() - Create a Line Segment Between (0,0) and a Point P
  263.  *
  264.  * This involves just creating and filling out a segment structure:
  265.  */
  266. struct segment *Line(
  267.         struct segment *P)        /* relevant coordinate space */
  268. {
  269.  
  270.     IfTrace1((MustTraceCalls), "..Line(%z)\n", P);
  271.     ARGCHECK(!ISLOCATION(P), "Line: arg not a location", P, NULL, (0,NULL,NULL,NULL), struct segment *);
  272.  
  273.     P = UniquePath(P);
  274.     P->type = LINETYPE;
  275.     return (P);
  276. }
  277.  
  278.  
  279. /*
  280.  * Curved Path Segments
  281.  *
  282.  * We need more points to describe curves.  So, the structures for curved
  283.  * path segments are slightly different.  The first part is identical;
  284.  * the curved structures are larger with the extra points on the end.
  285.  *
  286.  * Bezier Segment Structure
  287.  *
  288.  * We support third order Bezier curves.  They are specified with four
  289.  * control points A, B, C, and D.  The curve starts at A with slope AB
  290.  * and ends at D with slope CD.  The curvature at the point A is inversely
  291.  * related to the length |AB|, and the curvature at the point D is
  292.  * inversely related to the length |CD|.  Point A is always point (0,0).
  293.  */
  294.  
  295.  
  296. /*
  297.  * Bezier() - Generate a Bezier Segment
  298.  *
  299.  * This is just a simple matter of filling out a 'beziersegment' structure:
  300.  */
  301. struct beziersegment *Bezier(
  302.         struct segment *B,        /* second control point */
  303.         struct segment *C,        /* third control point */
  304.         struct segment *D)        /* fourth control point (ending point) */
  305. {
  306. /* added reference field of 1 to temporary template below 3-26-91  PNM */
  307.     static struct beziersegment template =
  308.     {BEZIERTYPE, 0, 1, sizeof(struct beziersegment), 0,
  309.      NULL, NULL,
  310.      {0, 0},
  311.      {0, 0},
  312.      {0, 0}};
  313.  
  314.     struct beziersegment *r;    /* output segment                  */
  315.  
  316.     IfTrace3((MustTraceCalls), "..Bezier(%z, %z, %z)\n", B, C, D);
  317.     ARGCHECK(!ISLOCATION(B), "Bezier: bad B", B, NULL, (2, C, D,NULL), struct beziersegment *);
  318.     ARGCHECK(!ISLOCATION(C), "Bezier: bad C", C, NULL, (2, B, D,NULL), struct beziersegment *);
  319.     ARGCHECK(!ISLOCATION(D), "Bezier: bad D", D, NULL, (2, B, C,NULL), struct beziersegment *);
  320.  
  321.     r = (struct beziersegment *)Allocate(sizeof(struct beziersegment), &template, 0);
  322.  
  323.     r->last = (struct segment *)r;
  324.     r->dest.x = D->dest.x;
  325.     r->dest.y = D->dest.y;
  326.     r->B.x = B->dest.x;
  327.     r->B.y = B->dest.y;
  328.     r->C.x = C->dest.x;
  329.     r->C.y = C->dest.y;
  330.  
  331.     ConsumePath(B);
  332.     ConsumePath(C);
  333.     ConsumePath(D);
  334.     return (r);
  335. }
  336.  
  337.  
  338. /*
  339.  * Font "Hint" Segments
  340.  *
  341.  * Hint() - A Font 'Hint' Segment
  342.  *
  343.  * This is temporary code while we experiment with hints.
  344.  */
  345. struct hintsegment *Hint(struct XYspace *S, double ref, double width, char orientation,
  346.                         char hinttype, char adjusttype, char direction, int label)
  347. {
  348. /* added reference field of 1 to hintsegment template below 3-26-91 PNM */
  349.     static struct hintsegment template =
  350.     {HINTTYPE, 0, 1, sizeof(struct hintsegment), 0,
  351.      NULL, NULL,
  352.      {0, 0},
  353.      {0, 0},
  354.      {0, 0},
  355.      ' ', ' ', ' ', ' ', 0};
  356.  
  357.     struct hintsegment *r;
  358.  
  359.     r = (struct hintsegment *)Allocate(sizeof(struct hintsegment), &template, 0);
  360.  
  361.     r->orientation = orientation;
  362.     if (width == 0.0)
  363.         width = 1.0;
  364.  
  365.     if (orientation == 'h')
  366.     {
  367.         (*S->convert) (&r->ref, S, 0.0, ref);
  368.         (*S->convert) (&r->width, S, 0.0, width);
  369.     }
  370.     else if (orientation == 'v')
  371.     {
  372.         (*S->convert) (&r->ref, S, ref, 0.0);
  373.         (*S->convert) (&r->width, S, width, 0.0);
  374.     }
  375.     else
  376.         return ((struct hintsegment *)ArgErr("Hint: orient not 'h' or 'v'", NULL, NULL));
  377.     if (r->width.x < 0)
  378.         r->width.x = -r->width.x;
  379.     if (r->width.y < 0)
  380.         r->width.y = -r->width.y;
  381.     r->hinttype = hinttype;
  382.     r->adjusttype = adjusttype;
  383.     r->direction = direction;
  384.     r->label = label;
  385.     r->last = (struct segment *)r;
  386.     ConsumeSpace(S);
  387.     return (r);
  388. }
  389.  
  390.  
  391. /*
  392.  * POP removes the first segment in a path 'p' and Frees it.  'p' is left
  393.  * pointing to the end of the path:
  394.  */
  395. #define POP(p) \
  396.      { register struct segment *linkp; \
  397.        linkp = p->link; \
  398.        if (linkp != NULL) \
  399.                linkp->last = p->last; \
  400.        Free(p); \
  401.        p = linkp; }
  402.  
  403.  
  404. /*
  405.  * INSERT inserts a single segment in the middle of a chain.  'b' is
  406.  * the segment before, 'p' the segment to be inserted, and 'a' the
  407.  * segment after.
  408.  */
  409. #define INSERT(b,p,a)  b->link=p; p->link=a; p->last=NULL
  410.  
  411.  
  412. /*
  413.  * Join() - Join Two Objects Together
  414.  *
  415.  * If these are paths, this operator simply invokes the CONCAT macro.
  416.  * Why so much code then, you ask?  Well we have to check for object
  417.  * types other than paths, and also check for certain path consistency
  418.  * rules.
  419.  */
  420. struct segment *t1_Join(struct segment *p1, struct segment *p2)
  421. {
  422.     IfTrace2((MustTraceCalls && PathDebug > 1), "..Join(%z, %z)\n", p1, p2);
  423.     IfTrace2((MustTraceCalls && PathDebug <= 1), "..Join(%x, %x)\n", p1, p2);
  424.  
  425. /* We start with a whole bunch of very straightforward argument tests: */
  426.     if (p2 != NULL)
  427.     {
  428.         if (!ISPATHTYPE(p2->type))
  429.         {
  430.  
  431.             if (p1 == NULL)
  432.                 return ((struct segment *)Unique(p2));
  433.  
  434.             switch (p1->type)
  435.             {
  436.  
  437.             case REGIONTYPE:
  438.  
  439.             case STROKEPATHTYPE:
  440.                 p1 = CoercePath(p1);
  441.                 break;
  442.  
  443.             default:
  444.                 return ((struct segment *)BegHandle(p1, p2));
  445.             }
  446.         }
  447.  
  448.         ARGCHECK((p2->last == NULL), "Join: right arg not anchor", p2, NULL, (1, p1,NULL,NULL), struct segment *);
  449.  
  450.         p2 = UniquePath(p2);
  451.  
  452. /*
  453.  * In certain circumstances, we don't have to duplicate a permanent
  454.  * location.  (We would just end up destroying it anyway).  These cases
  455.  * are when 'p2' begins with a move-type segment:
  456.  */
  457.         if (p2->type == TEXTTYPE || p2->type == MOVETYPE)
  458.         {
  459.             if (p1 == NULL)
  460.                 return (p2);
  461.             if (ISLOCATION(p1))
  462.             {
  463.                 p2->dest.x += p1->dest.x;
  464.                 p2->dest.y += p1->dest.y;
  465.                 ConsumePath(p1);
  466.                 return (p2);
  467.             }
  468.         }
  469.     }
  470.     else
  471.         return ((struct segment *)Unique(p1));
  472.  
  473.     if (p1 != NULL)
  474.     {
  475.         if (!ISPATHTYPE(p1->type))
  476.  
  477.             switch (p2->type)
  478.             {
  479.  
  480.             case REGIONTYPE:
  481.  
  482.             case STROKEPATHTYPE:
  483.                 p2 = CoercePath(p2);
  484.                 break;
  485.  
  486.             default:
  487.                 return ((struct segment *)EndHandle(p1, p2));
  488.             }
  489.  
  490.         ARGCHECK((p1->last == NULL), "Join: left arg not anchor", p1, NULL, (1, p2,NULL,NULL), struct segment *);
  491.  
  492.         p1 = UniquePath(p1);
  493.     }
  494.     else
  495.         return (p2);
  496.  
  497. /*
  498.  * At this point all the checking is done.  We have two temporary non-null
  499.  * path types in 'p1' and 'p2'.  If p1 ends with a MOVE, and p2 begins with
  500.  * a MOVE, we collapse the two MOVEs into one.  We enforce the rule that
  501.  * there may not be two MOVEs in a row:
  502.  */
  503.  
  504.     if (p1->last->type == MOVETYPE && p2->type == MOVETYPE)
  505.     {
  506.         p1->last->flag |= p2->flag;
  507.         p1->last->dest.x += p2->dest.x;
  508.         p1->last->dest.y += p2->dest.y;
  509.         POP(p2);
  510.         if (p2 == NULL)
  511.             return (p1);
  512.     }
  513. /*
  514.  * Now we check for another silly rule.  If a path has any TEXTTYPEs,
  515.  * then it must have only TEXTTYPEs and MOVETYPEs, and furthermore,
  516.  * it must begin with a TEXTTYPE.  This rule makes it easy to check
  517.  * for the special case of text.  If necessary, we will coerce
  518.  * TEXTTYPEs into paths so we don't mix TEXTTYPEs with normal paths.
  519.  */
  520.     if (p1->type == TEXTTYPE)
  521.     {
  522.         if (p2->type != TEXTTYPE && !ISLOCATION(p2))
  523.             p1 = CoerceText(p1);
  524.     }
  525.     else
  526.     {
  527.         if (p2->type == TEXTTYPE)
  528.         {
  529.             if (ISLOCATION(p1))
  530.             {
  531.                 p2->dest.x += p1->dest.x;
  532.                 p2->dest.y += p1->dest.y;
  533.                 Free(p1);
  534.                 return (p2);
  535.             }
  536.             else
  537.                 p2 = CoerceText(p2);
  538.         }
  539.     }
  540. /*
  541.  * Thank God!  Finally!  It's hard to believe, but we are now able to
  542.  * actually do the join.  This is just invoking the CONCAT macro:
  543.  */
  544.     CONCAT(p1, p2);
  545.  
  546.     return (p1);
  547. }
  548.  
  549.  
  550. /*
  551.  * JoinSegment() - Create a Path Segment and Join It to a Known Path
  552.  *
  553.  * This internal function is quicker than a full-fledged join because
  554.  * it can do much less checking.
  555.  */
  556. struct segment *t1_JoinSegment(
  557.         struct segment *before,    /* path to join before new segment */
  558.         int type,                /* type of new segment (MOVETYPE or LINETYPE) */
  559.         fractpel x, fractpel y,    /* x,y of new segment */
  560.         struct segment *after)    /* path to join after new segment */
  561. {
  562.     struct segment *r;    /* returned path built here              */
  563.  
  564.     r = PathSegment(type, x, y);
  565.     if (before != NULL)
  566.     {
  567.         CONCAT(before, r);
  568.         r = before;
  569.     }
  570.     else
  571.         r->context = after->context;
  572.     if (after != NULL)
  573.         CONCAT(r, after);
  574.     return (r);
  575. }
  576.  
  577.  
  578. /*
  579.  * Other Path Functions
  580.  */
  581.  
  582. struct segment *t1_ClosePath(
  583.         struct segment *p0,        /* path to close */
  584.         int lastonly)            /* flag deciding to close all subpaths or... */
  585. {
  586.     struct segment *p, *last, *start;    /* used in looping through path */
  587.     fractpel x, y;    /* current position in path                   */
  588.     fractpel firstx, firsty;    /* start position of sub path       */
  589.     struct segment *lastnonhint;    /* last non-hint segment in path */
  590.  
  591.     IfTrace1((MustTraceCalls), "ClosePath(%z)\n", p0);
  592.     if (p0 != NULL && p0->type == TEXTTYPE)
  593.         return (UniquePath(p0));
  594.     if (p0->type == STROKEPATHTYPE)
  595.         return ((struct segment *)Unique(p0));
  596.     /*
  597.      * NOTE: a null closed path is different from a null open path
  598.      * and is denoted by a closed (0,0) move segment.  We make
  599.      * sure this path begins and ends with a MOVETYPE:
  600.      */
  601.     if (p0 == NULL || p0->type != MOVETYPE)
  602.         p0 = JoinSegment(NULL, MOVETYPE, 0, 0, p0);
  603.     TYPECHECK("ClosePath", p0, MOVETYPE, NULL, (0,NULL,NULL,NULL), struct segment *);
  604.  
  605.     if (p0->last->type != MOVETYPE)
  606.         p0 = JoinSegment(p0, MOVETYPE, 0, 0, NULL);
  607.  
  608.     p0 = UniquePath(p0);
  609.  
  610. /*
  611.  * We now begin a loop through the path,
  612.  * incrementing current 'x' and 'y'.  We are searching
  613.  * for MOVETYPE segments (breaks in the path) that are not already closed.
  614.  * At each break, we insert a close segment.
  615.  */
  616.     for (p = p0, x = y = 0, start = NULL;
  617.          p != NULL;
  618.          x += p->dest.x, y += p->dest.y, last = p, p = p->link)
  619.     {
  620.  
  621.         if (p->type == MOVETYPE)
  622.         {
  623.             if (start != NULL && (lastonly ? p->link == NULL : TRUE) &&
  624.              !(ISCLOSED(start->flag) && LASTCLOSED(last->flag)))
  625.             {
  626.                 register struct segment *r;    /* newly created */
  627.  
  628.                 start->flag |= ISCLOSED(ON);
  629.                 r = PathSegment(LINETYPE, firstx - x,
  630.                         firsty - y);
  631.                 INSERT(last, r, p);
  632.                 r->flag |= LASTCLOSED(ON);
  633.                 /*< adjust 'last' if possible for a 0,0 close >*/
  634.                 {
  635.  
  636. #define   CLOSEFUDGE    3    /* if we are this close, let's change last segment */
  637.  
  638.                     if (r->dest.x != 0 || r->dest.y != 0)
  639.                     {
  640.                         if (r->dest.x <= CLOSEFUDGE && r->dest.x >= -CLOSEFUDGE
  641.                             && r->dest.y <= CLOSEFUDGE && r->dest.y >= -CLOSEFUDGE)
  642.                         {
  643.                             IfTrace2((PathDebug),
  644.                                  "ClosePath forced closed by (%p,%p)\n",
  645.                                  r->dest.x, r->dest.y);
  646.                             lastnonhint->dest.x += r->dest.x;
  647.                             lastnonhint->dest.y += r->dest.y;
  648.                             r->dest.x = r->dest.y = 0;
  649.                         }
  650.                     }
  651.                 }
  652.                 if (p->link != NULL)
  653.                 {
  654.                     p->dest.x += x - firstx;
  655.                     p->dest.y += y - firsty;
  656.                     x = firstx;
  657.                     y = firsty;
  658.                 }
  659.             }
  660.             start = p;
  661.             firstx = x + p->dest.x;
  662.             firsty = y + p->dest.y;
  663.         }
  664.         else if (p->type != HINTTYPE)
  665.             lastnonhint = p;
  666.     }
  667.     return (p0);
  668. }
  669.  
  670.  
  671. /*
  672.  * Reversing the Direction of a Path
  673.  *
  674.  * This turned out to be more difficult than I thought at first.  The
  675.  * trickiness was due to the fact that closed paths must remain closed,
  676.  * etc.
  677.  *
  678.  * We need three subroutines:
  679.  *
  680.  *  break a path at any point:
  681.  *     static struct segment *SplitPath();
  682.  *
  683.  *  break a path after first sub-path:
  684.  *     static struct segment *DropSubPath();
  685.  *
  686.  *  reverse a single sub-path:
  687.  *     static struct segment *ReverseSubPath();
  688.  */
  689.  
  690.  
  691. /*
  692.  * Reverse() - User Operator to Reverse a Path
  693.  *
  694.  * This operator reverses the entire path.
  695.  */
  696. struct segment *Reverse(
  697.         struct segment *p)        /* full path to reverse */
  698. {
  699.     struct segment *r;    /* output path built here              */
  700.     struct segment *nextp;    /* contains next sub-path            */
  701.  
  702.     IfTrace1((MustTraceCalls), "Reverse(%z)\n", p);
  703.  
  704.     if (p == NULL)
  705.         return (NULL);
  706.  
  707.     ARGCHECK(!ISPATHANCHOR(p), "Reverse: invalid path", p, NULL, (0,NULL,NULL,NULL), struct segment *);
  708.  
  709.     if (p->type == TEXTTYPE)
  710.         p = CoerceText(p);
  711.     p = UniquePath(p);
  712.  
  713.     r = NULL;
  714.  
  715.     do
  716.     {
  717.         nextp = DropSubPath(p);
  718.         p = ReverseSubPath(p);
  719.         r = Join(p, r);
  720.         p = nextp;
  721.  
  722.     }
  723.     while (p != NULL);
  724.  
  725.     return (r);
  726. }
  727.  
  728.  
  729. /*
  730.  * ReverseSubPath() - Subroutine to Reverse a Single Sub-Path
  731.  */
  732. static struct segment *ReverseSubPath(
  733.         struct segment *p)        /* input path */
  734. {
  735.     struct segment *r;    /* reversed path will be created here */
  736.     struct segment *nextp;    /* temporary variable used in loop */
  737.     int wasclosed;        /* flag, path was closed */
  738.  
  739.     if (p == NULL)
  740.         return (NULL);
  741.  
  742.     wasclosed = ISCLOSED(p->flag);
  743.     r = NULL;
  744.  
  745.     do
  746.     {
  747. /*
  748.  * First we reverse the direction of this segment and clean up its flags:
  749.  */
  750.         p->dest.x = -p->dest.x;
  751.         p->dest.y = -p->dest.y;
  752.         p->flag &= ~(ISCLOSED(ON) | LASTCLOSED(ON));
  753.  
  754.         switch (p->type)
  755.         {
  756.  
  757.         case LINETYPE:
  758.         case MOVETYPE:
  759.             break;
  760.  
  761.         case CONICTYPE:
  762.             {
  763. /*
  764.  * The logic of this is that the new M point (stored relative to the new
  765.  * beginning) is (M - C).  However, C ("dest") has already been reversed
  766.  * So, we add "dest" instead of subtracting it:
  767.  */
  768.                 register struct conicsegment *cp = (struct conicsegment *)p;
  769.  
  770.                 cp->M.x += cp->dest.x;
  771.                 cp->M.y += cp->dest.y;
  772.             }
  773.             break;
  774.  
  775.         case BEZIERTYPE:
  776.             {
  777.                 register struct beziersegment *bp = (struct beziersegment *)p;
  778.  
  779.                 bp->B.x += bp->dest.x;
  780.                 bp->B.y += bp->dest.y;
  781.                 bp->C.x += bp->dest.x;
  782.                 bp->C.y += bp->dest.y;
  783.             }
  784.             break;
  785.  
  786.         case HINTTYPE:
  787.             {
  788.                 register struct hintsegment *hp = (struct hintsegment *)p;
  789.  
  790.                 hp->ref.x = -hp->ref.x;
  791.                 hp->ref.y = -hp->ref.y;
  792.             }
  793.             break;
  794.  
  795.         default:
  796.             t1_abort("Reverse: bad path segment");
  797.         }
  798. /*
  799.  * We need to reverse the order of segments too, so we break this segment
  800.  * off of the input path, and tack it on the front of the growing path
  801.  * in 'r':
  802.  */
  803.         nextp = p->link;
  804.         p->link = NULL;
  805.         p->last = p;
  806.         if (r != NULL)
  807.             CONCAT(p, r);    /* leaves result in 'p'... not what we want */
  808.         r = p;
  809.         p = nextp;    /* advance to next segment in input path        */
  810.  
  811.     }
  812.     while (p != NULL);
  813.  
  814.     if (wasclosed)
  815.         r = ClosePath(r);
  816.  
  817.     return (r);
  818. }
  819.  
  820.  
  821. /*
  822.  * DropSubPath() - Drops the First Sub-Path Off a Path
  823.  *
  824.  * This subroutine returns the remaining sub-path(s).  While doing so, it
  825.  * breaks the input path after the first sub-path so that a pointer to
  826.  * the original path now contains the first sub-path only.
  827.  */
  828. static struct segment *DropSubPath(
  829.         struct segment *p0)        /* original path */
  830. {
  831.     struct segment *p;    /* returned remainder here               */
  832.  
  833.     for (p = p0; p->link != NULL; p = p->link)
  834.     {
  835.         if (p->link->type == MOVETYPE)
  836.             break;
  837.     }
  838.  
  839.     return (SplitPath(p0, p));
  840. }
  841.  
  842.  
  843. static struct segment *SplitPath(struct segment *anchor, struct segment *before)
  844. {
  845.     struct segment *r;
  846.  
  847.     if (before == anchor->last)
  848.         return (NULL);
  849.  
  850.     r = before->link;
  851.     r->last = anchor->last;
  852.     anchor->last = before;
  853.     before->link = NULL;
  854.  
  855.     return (r);
  856. }
  857.  
  858.  
  859. /*
  860.  * ReverseSubPaths() - Reverse the Direction of Sub-paths Within a Path
  861.  *
  862.  * This user operator reverses the sub-paths in a path, but leaves the
  863.  * 'move' segments unchanged.  It builds on top of the subroutines
  864.  * already established.
  865.  */
  866. struct segment *ReverseSubPaths(
  867.         struct segment *p)        /* input path */
  868. {
  869.     struct segment *r;    /* reversed path will be created here */
  870.     struct segment *nextp;    /* temporary variable used in loop */
  871.     int wasclosed;        /* flag; subpath was closed */
  872.     struct segment *nomove;    /* the part of sub-path without move segment */
  873.     struct fractpoint delta;
  874.  
  875.     IfTrace1((MustTraceCalls), "ReverseSubPaths(%z)\n", p);
  876.  
  877.     if (p == NULL)
  878.         return (NULL);
  879.  
  880.     ARGCHECK(!ISPATHANCHOR(p), "ReverseSubPaths: invalid path", p, NULL, (0,NULL,NULL,NULL), struct segment *);
  881.  
  882.     if (p->type == TEXTTYPE)
  883.         p = CoerceText(p);
  884.     if (p->type != MOVETYPE)
  885.         p = JoinSegment(NULL, MOVETYPE, 0, 0, p);
  886.  
  887.     p = UniquePath(p);
  888.  
  889.     r = NULL;
  890.  
  891.     for (; p != NULL;)
  892.     {
  893.         nextp = DropSubPath(p);
  894.         wasclosed = ISCLOSED(p->flag);
  895.         if (wasclosed)
  896.             UnClose(p);
  897.  
  898.         nomove = SplitPath(p, p);
  899.         r = Join(r, p);
  900.  
  901.         PathDelta(nomove, &delta);
  902.  
  903.         nomove = ReverseSubPath(nomove);
  904.         p->dest.x += delta.x;
  905.         p->dest.y += delta.y;
  906.         if (nextp != NULL)
  907.         {
  908.             nextp->dest.x += delta.x;
  909.             nextp->dest.y += delta.y;
  910.         }
  911.         if (wasclosed)
  912.         {
  913.             nomove = ClosePath(nomove);
  914.             nextp->dest.x -= delta.x;
  915.             nextp->dest.y -= delta.y;
  916.         }
  917.         r = Join(r, nomove);
  918.         p = nextp;
  919.  
  920.     }
  921.  
  922.     return (r);
  923. }
  924.  
  925.  
  926. static void UnClose(struct segment *p0)
  927. {
  928.     struct segment *p;
  929.  
  930.     for (p = p0; p->link->link != NULL; p = p->link)
  931.     {;
  932.     }
  933.  
  934.     if (!LASTCLOSED(p->link->flag))
  935.         t1_abort("UnClose:  no LASTCLOSED");
  936.  
  937.     Free(SplitPath(p0, p));
  938.     p0->flag &= ~ISCLOSED(ON);
  939. }
  940.  
  941.  
  942. /*
  943.  * Transforming and Putting Handles on Paths
  944.  *
  945.  * PathTransform() - Transform a Path
  946.  *
  947.  * Transforming a path involves transforming all the points.  In order
  948.  * that closed paths do not become "unclosed" when their relative
  949.  * positions are slightly changed due to loss of arithmetic precision,
  950.  * all point transformations are in absolute coordinates.
  951.  *
  952.  * (It might be better to reset the "absolute" coordinates every time a
  953.  * move segment is encountered.  This would mean that we could accumulate
  954.  * error from subpath to subpath, but we would be less likely to make
  955.  * the "big error" where our fixed point arithmetic "wraps".  However, I
  956.  * think I'll keep it this way until something happens to convince me
  957.  * otherwise.)
  958.  *
  959.  * The transform is described as a "space", that way we can use our
  960.  * old friend the "iconvert" function, which should be very efficient.
  961.  */
  962. struct segment *PathTransform(
  963.         struct segment *p0,        /* path to transform */
  964.         struct XYspace *S)        /* pseudo space to transform in */
  965. {
  966.     struct segment *p;    /* to loop through path with            */
  967.     fractpel newx, newy;    /* current transformed position in path */
  968.     fractpel oldx, oldy;    /* current untransformed position in path */
  969.     fractpel savex, savey;    /* save path delta x,y                */
  970.  
  971.     p0 = UniquePath(p0);
  972.  
  973.     newx = newy = oldx = oldy = 0;
  974.  
  975.     for (p = p0; p != NULL; p = p->link)
  976.     {
  977.  
  978.         savex = p->dest.x;
  979.         savey = p->dest.y;
  980.  
  981.         (*S->iconvert) (&p->dest, S, p->dest.x + oldx, p->dest.y + oldy);
  982.         p->dest.x -= newx;
  983.         p->dest.y -= newy;
  984.  
  985.         switch (p->type)
  986.         {
  987.  
  988.         case LINETYPE:
  989.         case MOVETYPE:
  990.             break;
  991.  
  992.         case CONICTYPE:
  993.             {
  994.                 register struct conicsegment *cp = (struct conicsegment *)p;
  995.  
  996.                 (*S->iconvert) (&cp->M, S, cp->M.x + oldx, cp->M.y + oldy);
  997.                 cp->M.x -= newx;
  998.                 cp->M.y -= newy;
  999.                 /*
  1000.                  * Note roundness doesn't change... linear transform
  1001.                  */
  1002.                 break;
  1003.             }
  1004.  
  1005.  
  1006.         case BEZIERTYPE:
  1007.             {
  1008.                 register struct beziersegment *bp = (struct beziersegment *)p;
  1009.  
  1010.                 (*S->iconvert) (&bp->B, S, bp->B.x + oldx, bp->B.y + oldy);
  1011.                 bp->B.x -= newx;
  1012.                 bp->B.y -= newy;
  1013.                 (*S->iconvert) (&bp->C, S, bp->C.x + oldx, bp->C.y + oldy);
  1014.                 bp->C.x -= newx;
  1015.                 bp->C.y -= newy;
  1016.                 break;
  1017.             }
  1018.  
  1019.         case HINTTYPE:
  1020.             {
  1021.                 register struct hintsegment *hp = (struct hintsegment *)p;
  1022.  
  1023.                 (*S->iconvert) (&hp->ref, S, hp->ref.x + oldx, hp->ref.y + oldy);
  1024.                 hp->ref.x -= newx;
  1025.                 hp->ref.y -= newy;
  1026.                 (*S->iconvert) (&hp->width, S, hp->width.x, hp->width.y);
  1027.                 /* Note: width is not relative to origin */
  1028.                 break;
  1029.             }
  1030.  
  1031.         case TEXTTYPE:
  1032.             {
  1033.                 XformText(p, S);
  1034.                 break;
  1035.             }
  1036.  
  1037.         default:
  1038.             IfTrace1(TRUE, "path = %z\n", p);
  1039.             t1_abort("PathTransform:  invalid segment");
  1040.         }
  1041.         oldx += savex;
  1042.         oldy += savey;
  1043.         newx += p->dest.x;
  1044.         newy += p->dest.y;
  1045.     }
  1046.     return (p0);
  1047. }
  1048.  
  1049.  
  1050. /*
  1051.  * PathDelta() - Return a Path's Ending Point
  1052.  */
  1053. void t1_PathDelta(
  1054.         struct segment *p,        /* input path */
  1055.         struct fractpoint *pt)    /* pointer to x,y to set */
  1056. {
  1057.     struct fractpoint mypoint;    /* I pass this to TextDelta               */
  1058.     fractpel x, y;    /* working variables for path current point   */
  1059.  
  1060.     for (x = y = 0; p != NULL; p = p->link)
  1061.     {
  1062.         x += p->dest.x;
  1063.         y += p->dest.y;
  1064.         if (p->type == TEXTTYPE)
  1065.         {
  1066.             TextDelta(p, &mypoint);
  1067.             x += mypoint.x;
  1068.             y += mypoint.y;
  1069.         }
  1070.     }
  1071.  
  1072.     pt->x = x;
  1073.     pt->y = y;
  1074. }
  1075.  
  1076.  
  1077. /*
  1078.  * BoundingBox() - Produce a Bounding Box Path
  1079.  *
  1080.  * This function is called by image code, when we know the size of the
  1081.  * image in pels, and need to get a bounding box path that surrounds it.
  1082.  * The starting/ending handle is in the lower right hand corner.
  1083.  */
  1084. struct segment *BoundingBox(
  1085.         pel h, pel w)            /* size of box */
  1086. {
  1087.     struct segment *path;
  1088.  
  1089.     path = PathSegment(LINETYPE, -TOFRACTPEL(w), 0);
  1090.     path = JoinSegment(NULL, LINETYPE, 0, -TOFRACTPEL(h), path);
  1091.     path = JoinSegment(NULL, LINETYPE, TOFRACTPEL(w), 0, path);
  1092.     path = ClosePath(path);
  1093.  
  1094.     return (path);
  1095. }
  1096.  
  1097.  
  1098. /*
  1099.  * Querying Locations and Paths
  1100.  *
  1101.  * QueryLoc() - Return the X,Y of a Locition
  1102.  */
  1103. void QueryLoc(
  1104.         struct segment *P,    /* location to query, not consumed */
  1105.         struct XYspace *S,    /* XY space to return coordinates in */
  1106.         double *xP, double *yP) /* coordinates returned here */
  1107. {
  1108.     IfTrace4((MustTraceCalls), "QueryLoc(P=%z, S=%z, (%x, %x))\n",
  1109.          P, S, xP, yP);
  1110.     if (!ISLOCATION(P))
  1111.     {
  1112.         ArgErr("QueryLoc: first arg not a location", P, NULL);
  1113.         return;
  1114.     }
  1115.     if (S->type != SPACETYPE)
  1116.     {
  1117.         ArgErr("QueryLoc: second arg not a space", S, NULL);
  1118.         return;
  1119.     }
  1120.     UnConvert(S, &P->dest, xP, yP);
  1121. }
  1122.  
  1123.  
  1124. /*
  1125.  * QueryPath() - Find Out the Type of Segment at the Head of a Path
  1126.  *
  1127.  * This is a very simple routine that looks at the first segment of a
  1128.  * path and tells the caller what it is, as well as returning the control
  1129.  * point(s) of the path segment.  Different path segments have different
  1130.  * number of control points.  If the caller knows that the segment is
  1131.  * a move segment, for example, he only needs to pass pointers to return
  1132.  * one control point.
  1133.  */
  1134. void QueryPath(
  1135.         struct segment *path,    /* path to check */
  1136.         int *typeP,        /* return the type of path here */
  1137.         struct segment **Bp,    /* return location of first point */
  1138.         struct segment **Cp,    /* return location of second point */
  1139.         struct segment **Dp,    /* return location of third point */
  1140.         double *fP)        /* return Conic sharpness */
  1141. {
  1142.     int coerced = FALSE;    /* did I coerce a text path?           */
  1143.  
  1144.     IfTrace3((MustTraceCalls), "QueryPath(%z, %x, %x, ...)\n",
  1145.          path, typeP, Bp);
  1146.     if (path == NULL)
  1147.     {
  1148.         *typeP = -1;
  1149.         return;
  1150.     }
  1151.     if (!ISPATHANCHOR(path))
  1152.     {
  1153.         ArgErr("QueryPath: arg not a valid path", path, NULL);
  1154.     }
  1155.     if (path->type == TEXTTYPE)
  1156.     {
  1157.         path = CoerceText(path);
  1158.         coerced = TRUE;
  1159.     }
  1160.  
  1161.     switch (path->type)
  1162.     {
  1163.  
  1164.     case MOVETYPE:
  1165.         *typeP = 0;
  1166.         *Bp = PathSegment(MOVETYPE, path->dest.x, path->dest.y);
  1167.         break;
  1168.  
  1169.     case LINETYPE:
  1170.         *typeP = (LASTCLOSED(path->flag)) ? 4 : 1;
  1171.         *Bp = PathSegment(MOVETYPE, path->dest.x, path->dest.y);
  1172.         break;
  1173.  
  1174.     case CONICTYPE:
  1175.         {
  1176.             register struct conicsegment *cp = (struct conicsegment *)path;
  1177.  
  1178.             *typeP = 2;
  1179.             *Bp = PathSegment(MOVETYPE, cp->M.x, cp->M.y);
  1180.             *Cp = PathSegment(MOVETYPE, cp->dest.x, cp->dest.y);
  1181.             *fP = cp->roundness;
  1182.         }
  1183.         break;
  1184.  
  1185.     case BEZIERTYPE:
  1186.         {
  1187.             register struct beziersegment *bp = (struct beziersegment *)path;
  1188.  
  1189.             *typeP = 3;
  1190.             *Bp = PathSegment(MOVETYPE, bp->B.x, bp->B.y);
  1191.             *Cp = PathSegment(MOVETYPE, bp->C.x, bp->C.y);
  1192.             *Dp = PathSegment(MOVETYPE, bp->dest.x, bp->dest.y);
  1193.         }
  1194.         break;
  1195.  
  1196.     case HINTTYPE:
  1197.         *typeP = 5;
  1198.         break;
  1199.  
  1200.     default:
  1201.         t1_abort("QueryPath: unknown segment");
  1202.     }
  1203.     if (coerced)
  1204.         KillPath(path);
  1205. }
  1206.  
  1207.  
  1208. /*
  1209.  * QueryBounds() - Return the Bounding Box of a Path
  1210.  *
  1211.  * Returns the bounding box by setting the user's variables.
  1212.  */
  1213. void QueryBounds(
  1214.         struct segment *p0,        /* object to check for bound */
  1215.         struct XYspace *S,        /* coordinate space of returned values */
  1216.         double *xminP,
  1217.         double *yminP,            /* lower left hand corner (set by routine) */
  1218.         double *xmaxP,
  1219.         double *ymaxP)            /* upper right hand corner (set by routine) */
  1220. {
  1221.     struct segment *path;    /* loop variable for path segments    */
  1222.     fractpel lastx, lasty;    /* loop variables:  previous endingpoint */
  1223.     fractpel x, y;        /* loop variables:  current ending point      */
  1224.     struct fractpoint min;    /* registers to keep lower left hand corner   */
  1225.     struct fractpoint max;    /* registers to keep upper right hand corner  */
  1226.     int coerced = FALSE;    /* we have coerced the path from another object */
  1227.     double x1, y1, x2, y2, x3, y3, x4, y4;    /* corners of rectangle in space X   */
  1228.  
  1229.     IfTrace2((MustTraceCalls), "QueryBounds(%z, %z,", p0, S);
  1230.     IfTrace4((MustTraceCalls), " %x, %x, %x, %x)\n",
  1231.          xminP, yminP, xmaxP, ymaxP);
  1232.     if (S->type != SPACETYPE)
  1233.     {
  1234.         ArgErr("QueryBounds:  bad XYspace", S, NULL);
  1235.         return;
  1236.     }
  1237.  
  1238.     min.x = min.y = max.x = max.y = 0;
  1239.     if (p0 != NULL)
  1240.     {
  1241.         if (!ISPATHANCHOR(p0))
  1242.         {
  1243.             switch (p0->type)
  1244.             {
  1245.             case STROKEPATHTYPE:
  1246.                 /* replaced DupStrokePath() with Dup() 3-26-91 PNM */
  1247.                 p0 = (struct segment *)DoStroke(Dup(p0));
  1248.                 /* no break here, we have a region in p0 */
  1249.             case REGIONTYPE:
  1250.                 p0 = RegionBounds(p0);
  1251.                 break;
  1252.  
  1253.             case PICTURETYPE:
  1254.                 p0 = PictureBounds(p0);
  1255.                 break;
  1256.  
  1257.             default:
  1258.                 ArgErr("QueryBounds:  bad object", p0, NULL);
  1259.                 return;
  1260.             }
  1261.             coerced = TRUE;
  1262.         }
  1263.         if (p0->type == TEXTTYPE)
  1264.         {
  1265.             /* replaced CopyPath() with Dup() 3-26-91 PNM */
  1266.             p0 = (struct segment *)CoerceText(Dup(p0));    /* there are faster ways */
  1267.             coerced = TRUE;
  1268.         }
  1269.         if (p0->type == MOVETYPE)
  1270.         {
  1271.             min.x = max.x = p0->dest.x;
  1272.             min.y = max.y = p0->dest.y;
  1273.         }
  1274.     }
  1275.     lastx = lasty = 0;
  1276.  
  1277.     for (path = p0; path != NULL; path = path->link)
  1278.     {
  1279.  
  1280.         x = lastx + path->dest.x;
  1281.         y = lasty + path->dest.y;
  1282.  
  1283.         switch (path->type)
  1284.         {
  1285.  
  1286.         case LINETYPE:
  1287.             break;
  1288.  
  1289.         case CONICTYPE:
  1290.             {
  1291.                 register struct conicsegment *cp = (struct conicsegment *)path;
  1292.                 register fractpel Mx = lastx + cp->M.x;
  1293.                 register fractpel My = lasty + cp->M.y;
  1294.                 register fractpel deltax = 0.5 * cp->roundness * cp->dest.x;
  1295.                 register fractpel deltay = 0.5 * cp->roundness * cp->dest.y;
  1296.                 register fractpel Px = Mx - deltax;
  1297.                 register fractpel Py = My - deltay;
  1298.                 register fractpel Qx = Mx + deltax;
  1299.                 register fractpel Qy = My + deltay;
  1300.  
  1301.  
  1302.                 if (Mx < min.x)
  1303.                     min.x = Mx;
  1304.                 else if (Mx > max.x)
  1305.                     max.x = Mx;
  1306.                 if (My < min.y)
  1307.                     min.y = My;
  1308.                 else if (My > max.y)
  1309.                     max.y = My;
  1310.  
  1311.                 if (Px < min.x)
  1312.                     min.x = Px;
  1313.                 else if (Px > max.x)
  1314.                     max.x = Px;
  1315.                 if (Py < min.y)
  1316.                     min.y = Py;
  1317.                 else if (Py > max.y)
  1318.                     max.y = Py;
  1319.  
  1320.                 if (Qx < min.x)
  1321.                     min.x = Qx;
  1322.                 else if (Qx > max.x)
  1323.                     max.x = Qx;
  1324.                 if (Qy < min.y)
  1325.                     min.y = Qy;
  1326.                 else if (Qy > max.y)
  1327.                     max.y = Qy;
  1328.             }
  1329.             break;
  1330.  
  1331.  
  1332.         case MOVETYPE:
  1333.             /*
  1334.                        * We can't risk adding trailing Moves to the
  1335.                        * bounding box:
  1336.                        */
  1337.             if (path->link == NULL)
  1338.                 goto done;    /* God forgive me                 */
  1339.             break;
  1340.  
  1341.         case BEZIERTYPE:
  1342.             {
  1343.                 register struct beziersegment *bp = (struct beziersegment *)path;
  1344.                 register fractpel Bx = lastx + bp->B.x;
  1345.                 register fractpel By = lasty + bp->B.y;
  1346.                 register fractpel Cx = lastx + bp->C.x;
  1347.                 register fractpel Cy = lasty + bp->C.y;
  1348.  
  1349.                 if (Bx < min.x)
  1350.                     min.x = Bx;
  1351.                 else if (Bx > max.x)
  1352.                     max.x = Bx;
  1353.                 if (By < min.y)
  1354.                     min.y = By;
  1355.                 else if (By > max.y)
  1356.                     max.y = By;
  1357.  
  1358.                 if (Cx < min.x)
  1359.                     min.x = Cx;
  1360.                 else if (Cx > max.x)
  1361.                     max.x = Cx;
  1362.                 if (Cy < min.y)
  1363.                     min.y = Cy;
  1364.                 else if (Cy > max.y)
  1365.                     max.y = Cy;
  1366.             }
  1367.             break;
  1368.  
  1369.         case HINTTYPE:
  1370.             break;
  1371.         default:
  1372.             t1_abort("QueryBounds: unknown type");
  1373.         }
  1374.  
  1375.         if (x < min.x)
  1376.             min.x = x;
  1377.         else if (x > max.x)
  1378.             max.x = x;
  1379.         if (y < min.y)
  1380.             min.y = y;
  1381.         else if (y > max.y)
  1382.             max.y = y;
  1383.  
  1384.         lastx = x;
  1385.         lasty = y;
  1386.     }
  1387.       done:
  1388.     UnConvert(S, &min, &x1, &y1);
  1389.     UnConvert(S, &max, &x4, &y4);
  1390.     x = min.x;
  1391.     min.x = max.x;
  1392.     max.x = x;
  1393.     UnConvert(S, &min, &x2, &y2);
  1394.     UnConvert(S, &max, &x3, &y3);
  1395.  
  1396.     *xminP = *xmaxP = x1;
  1397.     if (x2 < *xminP)
  1398.         *xminP = x2;
  1399.     else if (x2 > *xmaxP)
  1400.         *xmaxP = x2;
  1401.     if (x3 < *xminP)
  1402.         *xminP = x3;
  1403.     else if (x3 > *xmaxP)
  1404.         *xmaxP = x3;
  1405.     if (x4 < *xminP)
  1406.         *xminP = x4;
  1407.     else if (x4 > *xmaxP)
  1408.         *xmaxP = x4;
  1409.  
  1410.     *yminP = *ymaxP = y1;
  1411.     if (y2 < *yminP)
  1412.         *yminP = y2;
  1413.     else if (y2 > *ymaxP)
  1414.         *ymaxP = y2;
  1415.     if (y3 < *yminP)
  1416.         *yminP = y3;
  1417.     else if (y3 > *ymaxP)
  1418.         *ymaxP = y3;
  1419.     if (y4 < *yminP)
  1420.         *yminP = y4;
  1421.     else if (y4 > *ymaxP)
  1422.         *ymaxP = y4;
  1423.  
  1424.     if (coerced)
  1425.         Destroy(p0);
  1426. }
  1427.  
  1428.  
  1429. /*
  1430.  * BoxPath()
  1431.  */
  1432. struct segment *BoxPath(struct XYspace *S, int h, int w)
  1433. {
  1434.     struct segment *path;
  1435.  
  1436.     path = Join(Line(ILoc(S, w, 0)), Line(ILoc(S, 0, h)));
  1437.     path = JoinSegment(path, LINETYPE, -path->dest.x, -path->dest.y, NULL);
  1438.     return (ClosePath(path));
  1439. }
  1440.  
  1441.  
  1442. /*
  1443.  * DropSegment() - Drop the First Segment in a Path
  1444.  *
  1445.  * This routine takes the path and returns a new path that is one segment
  1446.  * shorter.  It can be used in conjunction with QueryPath(), for example,
  1447.  * to ask about an entire path.
  1448.  */
  1449. struct segment *DropSegment(struct segment *path)
  1450. {
  1451.     IfTrace1((MustTraceCalls), "DropSegment(%z)\n", path);
  1452.     if (path != NULL && path->type == STROKEPATHTYPE)
  1453.         path = CoercePath(path);
  1454.     ARGCHECK((path == NULL || !ISPATHANCHOR(path)),
  1455.          "DropSegment: arg not a non-null path", path, path, (0,NULL,NULL,NULL), struct segment *);
  1456.  
  1457.     if (path->type == TEXTTYPE)
  1458.         path = CoerceText(path);
  1459.     path = UniquePath(path);
  1460.  
  1461.     POP(path);
  1462.     return (path);
  1463. }
  1464.  
  1465.  
  1466. /*
  1467.  * HeadSegment() - Return the First Segment in a Path
  1468.  *
  1469.  * This routine takes the path and returns a new path consists of the
  1470.  * first segment only.
  1471.  */
  1472. struct segment *HeadSegment(
  1473.         struct segment *path)    /* input path */
  1474. {
  1475.     IfTrace1((MustTraceCalls), "HeadSegment(%z)\n", path);
  1476.     if (path == NULL)
  1477.         return (NULL);
  1478.     if (path->type == STROKEPATHTYPE)
  1479.         path = CoercePath(path);
  1480.     ARGCHECK(!ISPATHANCHOR(path), "HeadSegment: arg not a path", path, path, (0,NULL,NULL,NULL), struct segment *);
  1481.  
  1482.     if (path->type == TEXTTYPE)
  1483.         path = CoerceText(path);
  1484.     path = UniquePath(path);
  1485.  
  1486.     if (path->link != NULL)
  1487.         KillPath(path->link);
  1488.     path->link = NULL;
  1489.     path->last = path;
  1490.     return (path);
  1491. }
  1492.  
  1493.  
  1494. #ifdef AMISHDEBUG
  1495.  
  1496. /*
  1497.  * Path Debug Routines
  1498.  *
  1499.  * DumpPath() - Display a Path on the Trace File
  1500.  */
  1501. void DumpPath(struct segment *p)
  1502. {
  1503.     fractpel x, y;
  1504.     fractpel lastx, lasty;
  1505.     double roundness;
  1506.  
  1507.     IfTrace1(TRUE, "Dumping path, anchor=%x:\n", p);
  1508.     lastx = lasty = 0;
  1509.  
  1510.     for (; p != NULL; p = p->link)
  1511.     {
  1512.  
  1513.         IfTrace0(TRUE, ". ");
  1514.         x = p->dest.x;
  1515.         y = p->dest.y;
  1516.         switch (p->type)
  1517.         {
  1518.  
  1519.         case LINETYPE:
  1520.             IfTrace1(TRUE, ". line<%x> to", (long)p->flag);
  1521.             IfTrace4(TRUE, " (%p,%p), delta=(%p,%p)",
  1522.                  x + lastx, y + lasty, x, y);
  1523.             break;
  1524.  
  1525.         case MOVETYPE:
  1526.             IfTrace1(TRUE, "MOVE<%x> to", (long)p->flag);
  1527.             IfTrace4(TRUE, "(%p,%p), delta=(%p,%p)",
  1528.                  x + lastx, y + lasty, x, y);
  1529.             break;
  1530.  
  1531.         case CONICTYPE:
  1532.             {
  1533.                 register struct conicsegment *cp = (struct conicsegment *)p;
  1534.  
  1535.                 roundness = cp->roundness;
  1536.                 IfTrace2(TRUE, ". conic to (%p,%p),",
  1537.                      x + lastx, y + lasty);
  1538.                 IfTrace3(TRUE, " M=(%p,%p), r=%f", cp->M.x + lastx,
  1539.                      cp->M.y + lasty, &roundness);
  1540.             }
  1541.             break;
  1542.  
  1543.         case BEZIERTYPE:
  1544.             {
  1545.                 register struct beziersegment *bp = (struct beziersegment *)p;
  1546.  
  1547.                 IfTrace4(TRUE, ". bezier to (%p,%p), B=(%p,%p)",
  1548.                      x + lastx, y + lasty,
  1549.                      bp->B.x + lastx, bp->B.y + lasty);
  1550.                 IfTrace2(TRUE, ", C=(%p,%p)",
  1551.                      bp->C.x + lastx, bp->C.y + lasty);
  1552.             }
  1553.             break;
  1554.  
  1555.         case HINTTYPE:
  1556.             {
  1557.                 register struct hintsegment *hp = (struct hintsegment *)p;
  1558.  
  1559.                 IfTrace4(TRUE, ". hint ref=(%p,%p), width=(%p,%p)",
  1560.                        hp->ref.x + lastx, hp->ref.y + lasty,
  1561.                      hp->width.x, hp->width.y);
  1562.                 IfTrace4(TRUE, ", %c %c %c %c",
  1563.                      hp->orientation, hp->hinttype,
  1564.                      hp->adjusttype, hp->direction);
  1565.                 IfTrace1(TRUE, ", %ld", (long)hp->label);
  1566.             }
  1567.             break;
  1568.  
  1569.         case TEXTTYPE:
  1570. //            DumpText(p);
  1571. //            DumpText();
  1572.             break;
  1573.  
  1574.         default:
  1575.             IfTrace0(TRUE, "bad path segment?");
  1576.         }
  1577.         IfTrace1(TRUE, " at %x\n", p);
  1578.         lastx += x;
  1579.         lasty += y;
  1580.     }
  1581. }
  1582.  
  1583. #endif
  1584.